import sys

from aliyunsdkecs.request.v20140526 import DescribeImagesRequest
from aliyunsdkecs.request.v20140526 import CreateImageRequest
from aliyunsdkecs.request.v20140526 import DeleteImageRequest
from aliyunsdkecs.request.v20140526 import DescribeInstanceTypesRequest
from aliyunsdkslb.request.v20140515 import DescribeRegionsRequest

reload(sys)
sys.setdefaultencoding("utf-8")

from pprint import pprint

from libcloud.common.aliyun import AliyunConnection, AliyunDescribeInstances, AliyunGlobalConfig, \
    AliyunDescribeRegions, AliyunJsonResponse, ZoneType

try:
    import simplejson as json
except:
    import json

from aliyunsdkcore import client
from aliyunsdkecs.request.v20140526 import DescribeInstancesRequest

from libcloud.common.base import ConnectionKey
from libcloud.compute.base import NodeDriver, NodeSize, Node, NodeLocation, NodeImage, StorageVolume, VolumeSnapshot
from libcloud.compute.types import Provider, LibcloudError, NodeState

ECS_url = 'http://ecs.aliyuncs.com/'


class AliyunNode(Node):
    AliyunInstance = None

    # init
    def __init__(self, instanceAttributesType, driver):
        self.AliyunInstance = instanceAttributesType
        print "Class AliyunNode Method __init__ Debug:run"
        self.id = instanceAttributesType.InstanceId
        self.name = instanceAttributesType.InstanceName
        self.state = instanceAttributesType.Status
        self.public_ips = instanceAttributesType.PublicIpAddress
        self.private_ips = instanceAttributesType.InnerIpAddress
        self.driver = driver
        #TODO: need get size
        # self.size = AliyunNodeSize(instanceAttributesType.InstanceType, driver)
        self.extra = instanceAttributesType
        super(AliyunNode, self).__init__(
                self.id,
                self.name,
                self.state,
                self.public_ips,
                self.private_ips,
                self.driver,
                # self.size,
                # image=None,
                # self.extra
        )

    def get_uuid(self):
        return self.AliyunInstance.SerialNumber


class AliyunNodeSize(NodeSize):
    """
    A Base NodeSize class to derive from.

    NodeSizes are objects which are typically returned a driver's
    list_sizes function.  They contain a number of different
    parameters which define how big an image is.

    The exact parameters available depends on the provider.

    N.B. Where a parameter is "unlimited" (for example bandwidth in
    Amazon) this will be given as 0.

    >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
    >>> driver = DummyNodeDriver(0)
    >>> size = driver.list_sizes()[0]
    >>> size.ram
    128
    >>> size.bandwidth
    500
    >>> size.price
    4
    """

    def __init__(self, instanceTypeItemType, driver):
        self.id = instanceTypeItemType.InstanceTypeId
        self.name = instanceTypeItemType.InstanceTypeId
        self.ram = instanceTypeItemType.MemorySize # MB
        self.disk = None  # TODO:need get this disk
        self.bandwidth = None # TODO:need get network
        self.price = None  # TODO:get price and count dollars
        self.extra = instanceTypeItemType
        super(AliyunNodeSize, self).__init__(
                self.id,
                self.name,
                self.ram,
                self.disk,
                self.bandwidth,
                self.price,
                driver,
                self.extra)


class AliyunNodeLocation(NodeLocation):
    def __init__(self, zoneType, RegionId, driver):
        self.id = zoneType.ZoneId
        self.name = zoneType.LocalName
        self.country = RegionId # done:country
        self.driver = driver

        super(AliyunNodeLocation, self).__init__(
                self.id,
                self.name,
                self.country,
                self.driver
        )


class AliyunStorageVolume(StorageVolume):
    DiskItemType = None

    def __init__(self, diskItemType, driver):
        self.DiskItemType = diskItemType

        self.id = diskItemType.DiskId
        self.name = diskItemType.DiskName
        self.size = diskItemType.Size
        self.extra=diskItemType
        super(AliyunStorageVolume, self).__init__(
                self.id,
                self.name,
                self.size,
                driver,
                self.extra
        )


class AliyunVolumeSnapshot(VolumeSnapshot):
    def __init__(self,snapshotType,driver):
        self.id = snapshotType.SnapshotId
        self.size=snapshotType.SourceDiskSize
        self.extra = snapshotType
        self.created = snapshotType.CreationTime
        self.state = snapshotType.Status
        super(AliyunVolumeSnapshot,self).__init__(
                self.id,
                driver,
                self.size,
                self.state
        )

class AliyunNodeImage(NodeImage):
    def __init__(self, imageType, driver):
        """
        :param id: Image ID.
        :type id: ``str``

        :param name: Image name.
        :type name: ``str``

        :param driver: Driver this image belongs to.
        :type driver: :class:`.NodeDriver`

        :param extra: Optional provided specific attributes associated with
                      this image.
        :type extra: ``dict``
        """
        self.id = imageType.ImageId
        self.name = imageType.ImageName
        self.driver = driver
        self.extra = imageType
        # self.extra = extra or {} # TODO: extra
        super(AliyunNodeImage, self).__init__(
                self.id,
                self.name,
                self.driver,
                # self.extra
        )


class AliyunNodeDriver(NodeDriver):
    region_id = None
    name = "AliyunNodeDriver"
    # connectionCls = ConnectionKey
    connectionCls = AliyunConnection

    def __init__(self, access_key_id, access_key_secret, region_id, secure=True,
                 host=None, port=None, api_version=None, region=None, **kwargs):
        """
        :param    access_key_id:    Aliyun ECS API key to be used (required)
        :type     access_key_id:    ``str``

        :param    access_key_secret: Secret password to be used (required)
        :type     access_key_secret: ``str``

        :param regin_id: Optional driver region. Only used by drivers which
                       support multiple regions.
        :type regin_id: ``str``

        :param    secure: Weither to use HTTPS or HTTP. Note: Some providers
                            only support HTTPS, and it is on by default.
        :type     secure: ``bool``

        :param    host: Override hostname used for connections.
        :type     host: ``str``

        :param    port: Override port used for connections.
        :type     port: ``int``

        :param    api_version: Optional API version. Only used by drivers
                                 which support multiple API versions.
        :type     api_version: ``str``

        :rtype: ``None``
        """
        self.region_id = region_id
        super(AliyunNodeDriver, self).__init__(
                access_key_id,
                access_key_secret,
                secure,
                host,
                port,
                api_version,
                region
        )

    # end __init__

    def list_nodes(self):
        """
        List all nodes.

        :return:  list of node objects
        :rtype: ``list`` of :class:`AliyunNode`
        """
        list_of_node = []

        # request
        request = DescribeInstancesRequest.DescribeInstancesRequest()
        request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))

        # do_action
        httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

        # response get aliyunPage
        response = AliyunJsonResponse(httplib_response, self.connection)
        deserialize = response.AliyunPage()
        total_page = (deserialize.TotalCount / deserialize.PageSize) + 1

        # get objects
        for page in range(deserialize.PageNumber, total_page + 1):
            # request
            request = DescribeInstancesRequest.DescribeInstancesRequest()
            request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))
            request.set_PageNumber(page)
            # do_action
            httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

            # response
            response = AliyunJsonResponse(httplib_response, self.connection)
            deserialize = response.AliyunDescribeInstances()

            # append
            for instance in deserialize.Instances:
                aliyunNode = AliyunNode(instance, self)
                list_of_node.append(aliyunNode)

        # check total
        return list_of_node

    # end list_nodes

    # No In API
    def get_node(self, node_id):
        # request
        request = DescribeInstancesRequest.DescribeInstancesRequest()
        request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))

        # do_action
        httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

        # response get aliyunPage
        response = AliyunJsonResponse(httplib_response, self.connection)
        deserialize = response.AliyunDescribeInstances()

        # append
        for instance in deserialize.Instances:
            aliyunNode = AliyunNode(instance, self)
            return aliyunNode

        return None

    # No In API
    def has_node(self, aliyunNode):
        node = self.get_node(aliyunNode.id)

        if node is None:
            return False
        else:
            return True

    def list_sizes(self, location=None):
        """
        List sizes on a provider

        :param location: The location at which to list sizes
        :type location: :class:`.NodeLocation`

        :return: list of node size objects
        :rtype: ``list`` of :class:`.AliyunNodeSize`
        """
        # only from aliyun instancetype

        list_of_size = []

        # request
        request = DescribeInstanceTypesRequest.DescribeInstanceTypesRequest()
        request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))

        # do_action
        httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

        # response get aliyunPage
        response = AliyunJsonResponse(httplib_response, self.connection)
        deserialize = response.AliyunDescribeInstanceTypes()

        for instanceType in deserialize.InstanceTypes:
            aliyunNodeSize = AliyunNodeSize(instanceType, self)
            list_of_size.append(aliyunNodeSize)

        return list_of_size

    def list_locations(self):
        """
        List data centers for a provider

        :return: list of node location objects
        :rtype: ``list`` of :class:`.AliyunNodeLocation`
        """
        list_of_location = []

        # request
        request = DescribeRegionsRequest.DescribeRegionsRequest()
        request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))

        # do_action
        httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

        # response
        response = AliyunJsonResponse(httplib_response, self.connection)
        deserialize = response.AliyunDescribeRegions()

        for regionType in deserialize.Regions:

            # get zones
            # request
            zones = self.list_zones(regionType.RegionId)

            for zoneType in zones:
                location = AliyunNodeLocation(zoneType,regionType.RegionId,self)
                list_of_location.append(location)

        return list_of_location

    def get_location(self,node):

        locations = self.list_locations()
        for location in locations:
            if location.id == node.AliyunInstance.ZoneId:
                return location

        return None


    def list_zones(self,RegionId=None):
        """
        List data centers for a provider

        :param RegionId:
        :type RegionId: ``str``

        :return: list of node location objects
        :rtype: ``list`` of :class:`.ZoneType`
        """
        # request
        from aliyunsdkecs.request.v20140526 import DescribeZonesRequest
        request = DescribeZonesRequest.DescribeZonesRequest()
        request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))
        # do_action
        httplib_response = None
        if RegionId is None:
            httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)
        else:
            httplib_response = self.connection.get_client(RegionId).get_httplib_HTTPResponse(request)

        # response
        response = AliyunJsonResponse(httplib_response, self.connection)
        deserialize = response.AliyunDescribeZones()

        return deserialize.Zones


    def create_node(self, image,size,SecurityGroupId, **kwargs):
        """
        Create a new node instance. This instance will be started
        automatically.

        Not all hosting API's are created equal and to allow libcloud to
        support as many as possible there are some standard supported
        variations of ``create_node``. These are declared using a
        ``features`` API.
        You can inspect ``driver.features['create_node']`` to see what
        variation of the API you are dealing with:

        ``ssh_key``
            You can inject a public key into a new node allows key based SSH
            authentication.
        ``password``
            You can inject a password into a new node for SSH authentication.
            If no password is provided libcloud will generated a password.
            The password will be available as
            ``return_value.extra['password']``.
        ``generates_password``
            The hosting provider will generate a password. It will be returned
            to you via ``return_value.extra['password']``.

        Some drivers allow you to set how you will authenticate with the
        instance that is created. You can inject this initial authentication
        information via the ``auth`` parameter.

        If a driver supports the ``ssh_key`` feature flag for ``created_node``
        you can upload a public key into the new instance::

            >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
            >>> driver = DummyNodeDriver(0)
            >>> auth = NodeAuthSSHKey('pubkey data here')
            >>> node = driver.create_node("test_node", auth=auth)

        If a driver supports the ``password`` feature flag for ``create_node``
        you can set a password::

            >>> driver = DummyNodeDriver(0)
            >>> auth = NodeAuthPassword('mysecretpassword')
            >>> node = driver.create_node("test_node", auth=auth)

        If a driver supports the ``password`` feature and you don't provide the
        ``auth`` argument libcloud will assign a password::

            >>> driver = DummyNodeDriver(0)
            >>> node = driver.create_node("test_node")
            >>> password = node.extra['password']

        A password will also be returned in this way for drivers that declare
        the ``generates_password`` feature, though in that case the password is
        actually provided to the driver API by the hosting provider rather than
        generated by libcloud.

        You can only pass a :class:`.NodeAuthPassword` or
        :class:`.NodeAuthSSHKey` to ``create_node`` via the auth parameter if
        has the corresponding feature flag.

        :param name:   String with a name for this new node (required)
        :type name:   ``str``

        :param size:   The size of resources allocated to this node.
                            (required)
        :type size:   :class:`.NodeSize`

        :param image:  OS Image to boot on node. (required)
        :type image:  :class:`.NodeImage`

        :param location: Which data center to create a node in. If empty,
                              undefined behavior will be selected. (optional)
        :type location: :class:`.NodeLocation`

        :param auth:   Initial authentication information for the node
                            (optional)
        :type auth:   :class:`.NodeAuthSSHKey` or :class:`NodeAuthPassword`

        :return: The newly created node.
        :rtype: :class:`.Node`
        """
        # request
        from aliyunsdkecs.request.v20140526 import CreateInstanceRequest
        request = CreateInstanceRequest.CreateInstanceRequest()
        request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))
        request.set_ImageId(image.id)
        request.set_InstanceType(size.id)
        #TODO: SecurityGroups
        request.set_SecurityGroupId(SecurityGroupId)
        # do_action
        httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

        # response
        response = AliyunJsonResponse(httplib_response, self.connection)
        deserialize = response.AliyunRequestId()
        return True


    def list_securitygroups(self):
        list_of_securitygroup = []

        # request
        from aliyunsdkecs.request.v20140526 import DescribeSecurityGroupsRequest
        request = DescribeSecurityGroupsRequest.DescribeSecurityGroupsRequest()
        request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))

        # do_action
        httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

        # response
        response = AliyunJsonResponse(httplib_response, self.connection)
        deserialize = response.AliyunDescribeSecurityGroups()

        # response get aliyunPage
        response = AliyunJsonResponse(httplib_response, self.connection)
        deserialize = response.AliyunPage()
        total_page = (deserialize.TotalCount / deserialize.PageSize) + 1

        # get objects
        for page in range(deserialize.PageNumber, total_page + 1):
            # request
            request = DescribeSecurityGroupsRequest.DescribeSecurityGroupsRequest()
            request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))
            # if node is not None:
            #     request.set_InstanceId(node.id)
            request.set_PageNumber(page)
            # do_action
            httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

            # response
            response = AliyunJsonResponse(httplib_response, self.connection)
            deserialize = response.AliyunDescribeSecurityGroups()

            # append
            for SecurityGroup in deserialize.SecurityGroups:
                list_of_securitygroup.append(SecurityGroup)

        # check total
        return list_of_securitygroup


    def reboot_node(self, node, ForceStop=None):
        """
        Reboot a node.

        :param node: The node to be rebooted
        :type node: :class:`.AliyunNode`

        :param ForceStop: Force Stop the node
        :type ForceStop: ``bool``

        :return: True if the reboot was successful, otherwise False
        :rtype: ``bool``
        """
        # request
        from aliyunsdkecs.request.v20140526 import RebootInstanceRequest
        request = RebootInstanceRequest.RebootInstanceRequest()
        request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))
        request.set_InstanceId(node.id)
        print type(ForceStop)
        if ForceStop is not None:
            request.set_ForceStop(ForceStop)

        try:
            # do_action
            httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

            # response
            response = AliyunJsonResponse(httplib_response, self.connection)
            deserialize = response.AliyunRequestId()
            return True
        except:
            return False

            #TODO: not test
    def destroy_node(self, node, ForceStop=None):
        """
        Destroy a node.

        Depending upon the provider, this may destroy all data associated with
        the node, including backups.

        :param node: The node to be destroyed
        :type node: :class:`.AliyunNode`

        :param ForceStop: Force Stop the node
        :type ForceStop: ``bool``

        :return: True if the destroy was successful, False otherwise.
        :rtype: ``bool``
        """

        # request
        from aliyunsdkecs.request.v20140526 import StopInstanceRequest
        request = StopInstanceRequest.StopInstanceRequest()
        request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))
        request.set_InstanceId(node.id)
        print type(ForceStop)
        if ForceStop is not None:
            request.set_ForceStop(ForceStop)

        try:
            # do_action
            httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

            # response
            response = AliyunJsonResponse(httplib_response, self.connection)
            deserialize = response.AliyunRequestId()
            return True
        except:
            return False

    ##
    # Volume and snapshot management methods
    ##

    def list_volumes(self,node=None):
        """
        List storage volumes.

        :rtype: ``list`` of :class:`.StorageVolume`
        """
        list_of_volume = []

        # request
        from aliyunsdkecs.request.v20140526 import DescribeDisksRequest
        request = DescribeDisksRequest.DescribeDisksRequest()
        request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))
        if node is not None:
            request.set_InstanceId(node.id)
        # do_action
        httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

        # response get aliyunPage
        response = AliyunJsonResponse(httplib_response, self.connection)
        deserialize = response.AliyunPage()
        total_page = (deserialize.TotalCount / deserialize.PageSize) + 1

        # get objects
        for page in range(deserialize.PageNumber, total_page + 1):
            # request
            request = DescribeDisksRequest.DescribeDisksRequest()
            request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))
            if node is not None:
                request.set_InstanceId(node.id)
            request.set_PageNumber(page)
            # do_action
            httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

            # response
            response = AliyunJsonResponse(httplib_response, self.connection)
            deserialize = response.AliyunDescribeDisks()

            # append
            for disk in deserialize.Disks:
                aliyunStorageVolume = AliyunStorageVolume(disk, self)
                list_of_volume.append(aliyunStorageVolume)

        # check total
        return list_of_volume

    def get_volume(self,*volume_ids):
        """
        List storage volumes.

        :rtype: ``list`` of :class:`.StorageVolume`
        """
        list_of_volume = []

        # request
        from aliyunsdkecs.request.v20140526 import DescribeDisksRequest
        request = DescribeDisksRequest.DescribeDisksRequest()
        request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))
        j = json.dumps(volume_ids)
        request.set_DiskIds(j)
        # do_action
        httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

        # response get aliyunPage
        response = AliyunJsonResponse(httplib_response, self.connection)
        deserialize = response.AliyunPage()
        total_page = (deserialize.TotalCount / deserialize.PageSize) + 1

        # get objects
        for page in range(deserialize.PageNumber, total_page + 1):
            # request
            request = DescribeDisksRequest.DescribeDisksRequest()
            request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))
            j = json.dumps(volume_ids)
            request.set_DiskIds(j)
            request.set_PageNumber(page)
            # do_action
            httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

            # response
            response = AliyunJsonResponse(httplib_response, self.connection)
            deserialize = response.AliyunDescribeDisks()

            # append
            for disk in deserialize.Disks:
                aliyunStorageVolume = AliyunStorageVolume(disk, self)
                return aliyunStorageVolume


    def list_volume_snapshots(self, volume):
        """
        List snapshots for a storage volume.

        :rtype: ``list`` of :class:`VolumeSnapshot`
        """
        list_of_volume_snapshot = []

        # request
        from aliyunsdkecs.request.v20140526 import DescribeSnapshotsRequest
        request = DescribeSnapshotsRequest.DescribeSnapshotsRequest()
        request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))
        request.set_DiskId(volume.id)
        # do_action
        httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

        # response get aliyunPage
        response = AliyunJsonResponse(httplib_response, self.connection)
        deserialize = response.AliyunPage()
        total_page = (deserialize.TotalCount / deserialize.PageSize) + 1

        # get objects
        for page in range(deserialize.PageNumber, total_page + 1):
            # request
            request = DescribeSnapshotsRequest.DescribeSnapshotsRequest()
            request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))
            request.set_DiskId(volume.id)
            request.set_PageNumber(page)
            # do_action
            httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

            # response
            response = AliyunJsonResponse(httplib_response, self.connection)
            deserialize = response.AliyunDescribeSnapshots()

            # append
            for snapshot in deserialize.Snapshots:
                aliyunStorageVolume = AliyunVolumeSnapshot(snapshot, self)
                list_of_volume_snapshot.append(aliyunStorageVolume)

        # check total
        return list_of_volume_snapshot

    def get_volume_snapshots(self, *snapshot_ids):
        """
        List snapshots for a storage volume.

        :rtype: ``list`` of :class:`VolumeSnapshot`
        """
        list_of_volume_snapshot = []

        # request
        from aliyunsdkecs.request.v20140526 import DescribeSnapshotsRequest
        request = DescribeSnapshotsRequest.DescribeSnapshotsRequest()
        request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))
        j = json.dumps(snapshot_ids)
        request.set_SnapshotIds(j)
        # do_action
        httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

        # response get aliyunPage
        response = AliyunJsonResponse(httplib_response, self.connection)
        deserialize = response.AliyunPage()
        total_page = (deserialize.TotalCount / deserialize.PageSize) + 1

        # get objects
        for page in range(deserialize.PageNumber, total_page + 1):
            # request
            request = DescribeSnapshotsRequest.DescribeSnapshotsRequest()
            request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))
            j = json.dumps(snapshot_ids)
            request.set_SnapshotIds(j)
            request.set_PageNumber(page)
            # do_action
            httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

            # response
            response = AliyunJsonResponse(httplib_response, self.connection)
            deserialize = response.AliyunDescribeSnapshots()

            # append
            for snapshot in deserialize.Snapshots:
                aliyunStorageVolume = AliyunVolumeSnapshot(snapshot, self)
                list_of_volume_snapshot.append(aliyunStorageVolume)

        # check total
        return list_of_volume_snapshot

    def create_volume(self, size, name, location=None, snapshot=None):
        """
        Create a new volume.

        :param size: Size of volume in gigabytes (required)
        :type size: ``int``

        :param name: Name of the volume to be created
        :type name: ``str``

        :param location: Which data center to create a volume in. If
                               empty, undefined behavior will be selected.
                               (optional)
        :type location: :class:`.AliyunNodeLocation`

        :param snapshot:  Snapshot from which to create the new
                          volume.  (optional)
        :type snapshot: :class:`.AliyunVolumeSnapshot`

        :return: The newly created volume.
        :rtype: :class:`StorageVolume`
        """
        # request
        from aliyunsdkecs.request.v20140526 import CreateDiskRequest
        request = CreateDiskRequest.CreateDiskRequest()
        request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))
        request.set_Size(size)
        request.set_DiskName(name)
        if location is not None:
            request.set_ZoneId(location.id)
        else:
            zones = self.list_zones(self.connection.get_client().get_region_id())
            request.set_ZoneId(zones[0].ZoneId)

        if snapshot is not None:
            request.set_SnapshotId(snapshot.id)


        # do_action
        httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

        # response
        response = AliyunJsonResponse(httplib_response, self.connection)
        deserialize = response.parse_body()

        DiskId = deserialize['DiskId']

        #get volumne
        aliyunStorageVolume = self.get_volume(DiskId)
        return aliyunStorageVolume

    def create_volume_snapshot(self, volume, name=None):
        """
        Creates a snapshot of the storage volume.

        :param volume: The StorageVolume to create a VolumeSnapshot from
        :type volume: :class:`.VolumeSnapshot`

        :param name: Name of created snapshot (optional)
        :type name: `str`

        :rtype: :class:`VolumeSnapshot`
        """
        # request
        from aliyunsdkecs.request.v20140526 import CreateSnapshotRequest
        request = CreateSnapshotRequest.CreateSnapshotRequest()
        request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))
        request.set_DiskId(volume.id)

        if name is not None:
            request.set_SnapshotName(name)


        # do_action
        httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

        # response
        response = AliyunJsonResponse(httplib_response, self.connection)
        deserialize = response.parse_body()

        SnapshotId = deserialize['SnapshotId']

        snapshots =self.get_volume_snapshots(SnapshotId)
        return snapshots[0]

    def attach_volume(self, node, volume, device=None):
        """
        Attaches volume to node.

        :param node: Node to attach volume to.
        :type node: :class:`.Node`

        :param volume: Volume to attach.
        :type volume: :class:`.StorageVolume`

        :param device: Where the device is exposed, e.g. '/dev/sdb'
        :type device: ``str``

        :rytpe: ``bool``
        """
        # request
        from aliyunsdkecs.request.v20140526 import AttachDiskRequest
        request = AttachDiskRequest.AttachDiskRequest()
        request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))
        request.set_InstanceId(node.id)
        request.set_DiskId(volume.id)
        if device is not None:
            request.set_Device(device)

        # do_action
        httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

        # response
        response = AliyunJsonResponse(httplib_response, self.connection)
        deserialize = response.AliyunRequestId()
        return True
        # try:
        #     # do_action
        #     httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)
        #
        #     # response
        #     response = AliyunJsonResponse(httplib_response, self.connection)
        #     deserialize = response.AliyunRequestId()
        #     return True
        # except:
        #     return False

    def detach_volume(self, volume):
        """
        Detaches a volume from a node.

        :param volume: Volume to be detached
        :type volume: :class:`.AliyunStorageVolume`

        :rtype: ``bool``
        """
        # request
        from aliyunsdkecs.request.v20140526 import DetachDiskRequest
        request = DetachDiskRequest.DetachDiskRequest()
        request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))
        request.set_InstanceId(volume.DiskItemType.InstanceId)
        request.set_DiskId(volume.id)

        # try:
        # do_action
        httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

        # response
        response = AliyunJsonResponse(httplib_response, self.connection)
        deserialize = response.AliyunRequestId()
        return True
        #     return True
        # except:
        #     return False

    def destroy_volume(self, volume):
        """
        Destroys a storage volume.

        :param volume: Volume to be destroyed
        :type volume: :class:`StorageVolume`

        :rtype: ``bool``
        """
        # request
        from aliyunsdkecs.request.v20140526 import DeleteDiskRequest
        request = DeleteDiskRequest.DeleteDiskRequest()
        request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))
        request.set_DiskId(volume.id)

        try:
            # do_action
            httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

            # response
            response = AliyunJsonResponse(httplib_response, self.connection)
            deserialize = response.AliyunRequestId()
            return True
        except:
            return False

    def destroy_volume_snapshot(self, snapshot):
        """
        Destroys a snapshot.

        :param snapshot: The snapshot to delete
        :type snapshot: :class:`VolumeSnapshot`

        :rtype: :class:`bool`
        """
        raise NotImplementedError(
                'destroy_volume_snapshot not implemented for this driver')

    ##
    # Image management methods
    ##

    def list_images(self, location=None):
        """
        List images on a aliyun provider.

        :param location: The location at which to list images.
        :type location: :class:`.NodeLocation`

        :return: list of node image objects.
        :rtype: ``list`` of :class:`.AliyunNodeImage`
        """
        list_of_image = []

        # request
        request = DescribeImagesRequest.DescribeImagesRequest()
        request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))
        # do_action
        httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

        # response get aliyunPage
        response = AliyunJsonResponse(httplib_response, self.connection)
        deserialize = response.AliyunPage()
        total_page = (deserialize.TotalCount / deserialize.PageSize) + 1

        # get objects
        for page in range(deserialize.PageNumber, total_page + 1):
            request = DescribeImagesRequest.DescribeImagesRequest()
            request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))
            request.set_PageNumber(page)

            httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

            # response
            response = AliyunJsonResponse(httplib_response, self.connection)
            deserialize = response.AliyunDescribeImages()

            # append
            for image in deserialize.Images:
                aliyunNodeImage = AliyunNodeImage(image, self)
                list_of_image.append(aliyunNodeImage)

        # check total
        return list_of_image

    def create_image(self, node, name, description=None):
        """
        Creates an image from a node object.

        :param node: Node to run the task on.
        :type node: :class:`.AliyunNode`

        :param name: name for new image.
        :type name: ``str``

        :param description: description for new image.
        :type name: ``description``

        :rtype: :class:`.AliyunNodeImage`:
        :return: NodeImage instance on success.

        """
        # check has this node
        check_result = self.has_node(node)
        if check_result == False:
            raise Exception('no has input node :[node.id]:' + node.id)
        else:
            # request
            request = CreateImageRequest.CreateImageRequest()
            request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))
            # request.set
            # TODO:Snapshot
            # request.set_SnapshotId()
            request.set_ImageName(name)
            request.set_Description(description)
            # do_action
            httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

            # response get aliyunPage
            response = AliyunJsonResponse(httplib_response, self.connection)
            deserialize = response.parse_body()
            ImageId = deserialize['ImageId']

            # find image
            return self.get_image(ImageId)
            # try create image

            # return
            # raise NotImplementedError(
            #         'create_image not implemented for this driver')

    def delete_image(self, node_image):
        """
        Deletes a node image from a provider.

        :param node_image: Node image object.
        :type node_image: :class:`.AliyunNodeImage`

        :return: ``True`` if delete_image was successful, ``False`` otherwise.
        :rtype: ``bool``
        """
        request = DeleteImageRequest.DeleteImageRequest()
        request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))
        request.set_ImageId(node_image.id)
        # do_action
        try:
            httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

            # response get aliyunPage
            response = AliyunJsonResponse(httplib_response, self.connection)
            deserialize = response.AliyunRequestId()
            return True
        except:
            return False

    def get_image(self, image_id):
        """
        Returns a single node image from a provider.

        :param image_id: Node to run the task on.
        :type image_id: ``str``

        :rtype :class:`.NodeImage`:
        :return: NodeImage instance on success.
        """
        request = DescribeImagesRequest.DescribeImagesRequest()
        request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))
        request.set_ImageId(image_id)
        # do_action
        httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

        # response get aliyunPage
        response = AliyunJsonResponse(httplib_response, self.connection)
        deserialize = response.AliyunPage()
        if deserialize.TotalCount == 1:
            deserialize = response.AliyunDescribeImages()
            for image in deserialize.Images:
                aliyunNodeImage = AliyunNodeImage(image, self)
                return aliyunNodeImage
        else:
            raise Exception('Server response error: TotalCount not 1')

    def copy_image(self, source_region, node_image, name=None, description=None):
        """
        Copies an image from a source region to the current region.

        :param source_region: Region to copy the node from.
        :type source_region: ``str``

        :param node_image: NodeImage to copy.
        :type node_image: :class:`.NodeImage`:

        :param name: name for new image.
        :type name: ``str``

        :param description: description for new image.
        :type name: ``str``

        :rtype: :class:`.NodeImage`:
        :return: NodeImage instance on success.
        """
        from aliyunsdkecs.request.v20140526 import CopyImageRequest
        request = CopyImageRequest.CopyImageRequest()
        request.set_accept_format(AliyunGlobalConfig.Format.get('JSON'))
        request.set_DestinationRegionId(source_region)
        request.set_ImageId(node_image.id)
        request.set_DestinationImageName(name)
        request.set_DestinationDescription(description)
        # do_action
        httplib_response = self.connection.get_client().get_httplib_HTTPResponse(request)

        # response get aliyunPage
        response = AliyunJsonResponse(httplib_response, self.connection)
        deserialize = response.parse_body()
        ImageId = deserialize['ImageId']

        images = self.list_images()
        for image in images:
            if image.id == ImageId:
                return image

    ##
    # SSH key pair management methods
    ##